
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Text;

namespace Swaf
{
	/// <summary>
	/// This class represents a collection of boolean values stored as bits in a dynamic 
	/// array of char elements.  The array of chars is sized to be only big enough for the 
	/// number of bits set.  It dynamically sizes itself as calls to its setBit are done.
	/// 
	/// If created with its default constructor this class will have a null array.  If
	/// the clear method is called it will delete the array.  
	/// </summary>
	[Serializable]
	public class BitFlags
	{
		protected bool m_default = false;
		protected byte[] m_bits = null;

		/// <summary>
		/// Initializes the object with an empty set of bits and no bit set to true.</summary>
		public BitFlags()
		{
			//Intentionally left blank
		}

		public BitFlags(bool defaultVal)
		{
			m_default = defaultVal;
		}

		public bool DefaultValue { get { return m_default; } set { m_default = value; } }

		/// <summary>
		/// The class support for creating an initial value for the BitFlags byte array.
		/// (e.g. when creating 5 byte array to store the bits you can initialize the class by
		/// passing the param as "0,0,0,0,0")
		/// the example above will then create a five elements in the array.
		/// to set the third element value of the array to true you can then call the
		/// setBit method like setBit(32,true)</summary>
		/// <param name="arrayInfo">a string of byte values separated by comma.</param>
		public BitFlags(string arrayInfo)
		{
			if (arrayInfo != null && arrayInfo.Length != 0)
			{
				try
				{
					string[] sa = arrayInfo.Split(',');
					m_bits = new byte[sa.Length];

					for (int i = 0; i < sa.Length; i++)
						m_bits[i] = byte.Parse(sa[i]);
				}
				catch (Exception e)
				{
					throw new BaseException(
						String.Format("Invalid BitFalgs initialization value '{0}' specified", arrayInfo),
						e);
				}
			}
		}

		/// <summary>
		/// returns a string representation of the current BitFlag Array.</summary>
		/// <returns>The string of bytes from the bit array in a comma delimited format</returns>
		public string getArrayInfo()
		{
			if (m_bits == null)
				return "";

			StringBuilder buff = new StringBuilder(m_bits.Length * 4);
			foreach (byte b in m_bits)
				buff.Append(b.ToString()).Append(",");
			return buff.ToString(0, buff.Length - 1);
		}

		/// <summary>
		/// returns boolean status of a given bit index.  If the index
		/// requested is outside of the range of bits set, false is returned.</summary>
		public bool getBit(int index)
		{
			if (m_bits == null || (index / 8M) >= m_bits.Length)
				return m_default;

			int arrayIdx = index / 8;
			int bitIndex = (index % 8);

			return ((m_bits[arrayIdx] >> bitIndex) & 1) != 0;

		}

		/// <summary>
		/// Sets all bits to false by setting the internal byte array to null.
		/// </summary>
		public void Clear()
		{
			m_bits = null;
		}

		/// <summary>
		/// checks whether any of the array value had been set with value != 0</summary>
		public bool AnySet
		{
			get
			{
				if (m_bits != null)
					foreach (byte b in m_bits)
						if (b != 0)
							return true;
				return m_default;
			}
		}

		/// <summary>
		/// change the bit value of an array element using the shift bit operator.
		/// this can be use to track down dirty field value of a bizObj</summary>
		/// <param name="index">array element index</param>
		/// <param name="bit">set the element value to either true or false</param>
		public void setBit(int index, bool bit)
		{
			if (index >= 0)
			{
				int arrayIdx = (index / 8);
				int bitIndex = (index % 8);


				if (m_bits == null)
					m_bits = new byte[arrayIdx + 1];
				else if ((m_bits.Length - 1) < arrayIdx)
				{
					byte[] tmp = new byte[arrayIdx + 1];
					m_bits.CopyTo(tmp, 0);
					m_bits = tmp;
				}

				if (bit)
					m_bits[arrayIdx] |= (byte)((byte)1 << bitIndex);
				else
					m_bits[arrayIdx] &= (byte)~((byte)1 << bitIndex);
			}
		}
	}
}
